/** \file
* \par Subversion URL:
* \$URL: http://planar-rendering.googlecode.com/svn/trunk/engine/src/context.hpp $
* \version \$Rev: 76 $
* \date    \$Date: 2009-04-20 02:26:00 +0000 (Mon, 20 Apr 2009) $
*/

#pragma once

#include "functions.hpp"

/// <summary>
/// Contains context for a fractal generation session.
/// </summary>
class Context
{
public:
	/// <summary>
	/// Enumeration describing the different complex functions available.
	/// </summary>
	enum Func
	{
		/// <summary>The function list terminator.</summary>
		func_end,
		/// <summary>Take the absolute value of each quaternion vector component.</summary>
		func_zabscomps,
		/// <summary>Square the quaternion.</summary>
		func_zpow2,
		/// <summary>Take the dth power of the quaternion.</summary>
		func_zpowd,
		/// <summary>Add S to the quaternion.</summary>
		func_zpluss,
		/// <summary>Add some constant C to the quaternion.</summary>
		func_zplusc
	};

	/// <summary>
	/// Run through an iteration of the innermost parametric range.
	/// </summary>
	/// <typeparam name="Space">The complex space to use.</typeparam>
	/// <param name="funcs">The list of functions to apply.</param>
	/// <param name="parms">The list of parameter values to use.</param>
	/// <param name="row">The array to which the membership bools will be written.</param>
	template <Space space>
	inline void perform(const Func *funcs, double *parms, bool *row)
	{
		IterateContext ic;
		for (int i = 0; i < 4; i++) ic.S[i] = parms+i;
		double &p = parms[pindex];

		int pi = 0;
		for (p = plow;; p += pdelta)
		{
			ic.Reset();
			ic.SetSquares();
			for (int i = 0;;)
			{
				double *pparms = parms+4;
				for (const Func *func = funcs;; func++)
				{
					switch (*func)
					{
					case func_zabscomps:	iterate<space, zabscomps>(ic, pparms); break;
					case func_zpow2:		iterate<space, zpow2>    (ic, pparms); break;
					case func_zpowd:		iterate<space, zpowd>    (ic, pparms); break;
					case func_zpluss:		iterate<space, zpluss>   (ic, pparms); break;
					case func_zplusc:		iterate<space, zplusc>   (ic, pparms); break;
					default:
						goto donefuncs;
					}
					ic.SetSquares();	// For now, for safety set squares after every operation
				}
				donefuncs:

				if (ic.Modulus() >= 4)
				{
					row[pi] = false;
					break;
				}
				i++;
				if (i >= imax)
				{
					row[pi] = true;
					break;
				}
			}

			pi++;
			if (pi >= pres)
				return;
		}
	}

	/// <summary>
	/// The inner iteration code. Applies the specified function.
	/// <typeparam name="Space">The complex space to use.</typeparam>
	/// <typeparam name="FunctionType">The function to apply.</typeparam>
	/// <param name="ic">The iteration context instance to modify.</param>
	/// <param name="pparms">A reference to the parameter list pointer; this will be advanced.</param>
	template <Space space, typename FunctionType>
	inline void iterate(IterateContext &ic, double *&pparms)
	{
		FunctionType *const func = (FunctionType*)pparms;
		pparms += sizeof(FunctionType)/sizeof(double);
		func->perform<space>(ic);
	}

	// Members passed from  managed code

	/// <summary>The maximum number of iterations to perform.</summary>
	int imax;
	/// <summary>The index in the parameter array to the innermost parameter.</summary>
	int pindex;
	/// <summary>The resolution of the innermost parameter.</summary>
	int pres;
	/// <summary>The low value of the innermost range.</summary>
	double plow;
	/// <summary>The delta value of the innermost range.</summary>
	double pdelta;
};
